{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\sklearn\\cross_validation.py:41: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.\n",
      "  \"This module will be removed in 0.20.\", DeprecationWarning)\n"
     ]
    }
   ],
   "source": [
    "#-*- coding=utf-8 -*-\n",
    "import xml.etree.ElementTree as ET\n",
    "import glob,os\n",
    "import pprint\n",
    "import pandas as pd\n",
    "import time\n",
    "import random\n",
    "import pickle\n",
    "import cv2\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import json\n",
    "import shutil\n",
    "import math\n",
    "import datetime\n",
    "from sklearn import cross_validation,metrics\n",
    "from scipy import interp\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn import svm, datasets\n",
    "from sklearn.metrics import roc_curve, auc\n",
    "from sklearn.cross_validation import StratifiedKFold\n",
    "\n",
    "\n",
    "seed=17\n",
    "random.seed(seed)\n",
    "np.random.seed(seed)\n",
    "\n",
    "\n",
    "#遍历指定文件夹包括其子文件夹，寻找某种后缀的文件，并返回找到的文件路径列表\n",
    "def traverse_dir_suffix(dirPath, suffix):\n",
    "    suffixList = []\n",
    "    for (root, dirs, files)in os.walk(dirPath):\n",
    "            findList = glob.glob(root+'\\\\*.'+suffix)\n",
    "            for f in findList:\n",
    "                suffixList.append(f)\n",
    "    return suffixList\n",
    "\n",
    "#读取1个xml文件，输出瑕疵坐标列表和瑕疵类型列表，格式为：[[瑕疵类型1，瑕疵1坐标，瑕疵1面积占比]，...]；\n",
    "def read_xml(xmlPath):\n",
    "    tree = ET.parse(xmlPath)\n",
    "    root = tree.getroot()\n",
    "    defectList = []\n",
    "    for child in root.findall('object'):\n",
    "        bndbox=child.find('bndbox')\n",
    "        bndboxXY = [int(bndbox.find('xmin').text),int(bndbox.find('ymin').text),\n",
    "                    int(bndbox.find('xmax').text),int(bndbox.find('ymax').text)]\n",
    "        defectList.append([child.find('name').text, bndboxXY, 1.0])\n",
    "    return defectList\n",
    "\n",
    "def gen_xmlDict(xmlPath):\n",
    "    #读取所有xml文件，若xml文件存在，则为瑕疵图片，xml文件不存在，则为正常图片，\n",
    "    #生成的每张图片的Dict格式如下：\n",
    "    #{\"isNormalImg\": false, \"defectList\": [[\"油渍\", [1113, 812, 1598, 1273], 1.0], [\"线印\", [918, 427, 1003, 546], 1.0], [\"线印\", [1059, 436, 1132, 515], 1.0]]}\n",
    "    imgPath = xmlPath.replace('xml', 'jpg')\n",
    "    #h,w,c = cv_imread(imgPath).shape\n",
    "    h,w=1920,2560\n",
    "    xmlDict = {}\n",
    "    filename = os.path.split(imgPath)[1]\n",
    "    #xmlDict['imgPath'] = imgPath\n",
    "    if os.path.exists(xmlPath):\n",
    "        xmlDict['isNormalImg'] = False\n",
    "        #xmlDict['xmlPath'] = xmlPath\n",
    "        defectList = read_xml(xmlPath)\n",
    "    else:\n",
    "        xmlDict['isNormalImg'] = True\n",
    "        #xmlDict['xmlPath'] = ''\n",
    "        defectList = [[\"正常\", [0, 0, w, h], 1.0]]\n",
    "    #xmlDict['filename'] = filename \n",
    "    #xmlDict['imgPath'] = imgPath \n",
    "    xmlDict['defectList'] = defectList \n",
    "    return xmlDict\n",
    "    \n",
    "def cal_area(box):#box = [xmin,ymin,xmax,ymax]#用于计算box的面积\n",
    "    if box == []:\n",
    "        area =0\n",
    "    else:\n",
    "        [xb1,yb1,xb2,yb2] = box\n",
    "        area = (xb2-xb1)*(yb2-yb1)\n",
    "    return area \n",
    "\n",
    "def gen_cutDict(cutXY, defectList):\n",
    "    #用于生成切割后图片信息，给定切割的坐标和图片中瑕疵信息，就能判断该切割图片中是否包含瑕疵，包含的瑕疵面积占完整瑕疵总面积的比例\n",
    "    [x1,y1,x2,y2] = cutXY \n",
    "    cutDict={}\n",
    "    cutDefectList=[]\n",
    "    for defect in defectList:\n",
    "        cutDefect=[]\n",
    "        defectType, defectBox, defectRatio = defect\n",
    "        [xb1,yb1,xb2,yb2] = defectBox\n",
    "        defectArea = cal_area(defectBox)\n",
    "        assert x1<x2 and y1<y2 and xb1<xb2 and yb1<yb2, 'x1<x2, y1<y2 need to be satisfied'\n",
    "        if x2<=xb1 or xb2<=x1 or y2<=yb1 or yb2<=y1:#bndbox在切割后的图片外面\n",
    "            cutDefectBox = []\n",
    "        else:\n",
    "            if xb1<=x1 and x1<=xb2 and xb2<=x2 and yb1<=y1 and y1<=yb2 and yb2<=y2:#1-4:xb1<=x1<=xb2<=x2\n",
    "                cutDefectBox = [x1,y1,xb2,yb2]\n",
    "    \n",
    "            elif xb1<=x1 and x1<=xb2 and xb2<=x2 and y1<=yb1 and yb1<=yb2 and yb2<=y2:\n",
    "                cutDefectBox = [x1,yb1,xb2,yb2]\n",
    "    \n",
    "            elif xb1<=x1 and x1<=xb2 and xb2<=x2 and y1<=yb1 and yb1<=y2 and y2<=yb2:\n",
    "                cutDefectBox = [x1,yb1,xb2,y2]\n",
    "    \n",
    "            elif xb1<=x1 and x1<=xb2 and xb2<=x2 and yb1<=y1 and y1<=y2 and y2<=yb2:\n",
    "                cutDefectBox = [x1,y1,xb2,y2]\n",
    "    \n",
    "            elif x1<=xb1 and xb1<=xb2 and xb2<=x2 and yb1<=y1 and y1<=yb2 and yb2<=y2:#5-8:x1<=xb1<=xb2<=x2\n",
    "                cutDefectBox = [xb1,y1,xb2,yb2]\n",
    "\n",
    "            elif x1<=xb1 and xb1<=xb2 and xb2<=x2 and y1<=yb1 and yb1<=yb2 and yb2<=y2:\n",
    "                cutDefectBox = [xb1,yb1,xb2,yb2]\n",
    "        \n",
    "            elif x1<=xb1 and xb1<=xb2 and xb2<=x2 and y1<=yb1 and yb1<=y2 and y2<=yb2:\n",
    "                cutDefectBox = [xb1,yb1,xb2,y2]\n",
    "    \n",
    "            elif x1<=xb1 and xb1<=xb2 and xb2<=x2 and yb1<=y1 and y1<=y2 and y2<=yb2:\n",
    "                cutDefectBox = [xb1,y1,xb2,y2]\n",
    "    \n",
    "            elif x1<=xb1 and xb1<=x2 and x2<=xb2 and yb1<=y1 and y1<=yb2 and yb2<=y2:#9-12:x1<=xb1<=x2<=xb2\n",
    "                cutDefectBox = [xb1,y1,x2,yb2]\n",
    "            \n",
    "            elif x1<=xb1 and xb1<=x2 and x2<=xb2 and y1<=yb1 and yb1<=yb2 and yb2<=y2:\n",
    "                cutDefectBox = [xb1,yb1,x2,yb2]\n",
    "    \n",
    "            elif x1<=xb1 and xb1<=x2 and x2<=xb2 and y1<=yb1 and yb1<=y2 and y2<=yb2:\n",
    "                cutDefectBox = [xb1,yb1,x2,y2]\n",
    "    \n",
    "            elif x1<=xb1 and xb1<=x2 and x2<=xb2 and yb1<=y1 and y1<=y2 and y2<=yb2:\n",
    "                cutDefectBox = [xb1,y1,x2,y2]\n",
    "    \n",
    "            elif xb1<=x1 and x1<=x2 and x2<=xb2 and yb1<=y1 and y1<=yb2 and yb2<=y2:#13-16:xb1<=x1<=x2<=xb2\n",
    "                cutDefectBox = [x1,y1,x2,yb2]\n",
    "    \n",
    "            elif xb1<=x1 and x1<=x2 and x2<=xb2 and y1<=yb1 and yb1<=yb2 and yb2<=y2:\n",
    "                cutDefectBox = [x1,yb1,x2,yb2]\n",
    "    \n",
    "            elif xb1<=x1 and x1<=x2 and x2<=xb2 and y1<=yb1 and yb1<=y2 and y2<=yb2:\n",
    "                cutDefectBox = [x1,yb1,x2,y2]\n",
    "        \n",
    "            elif xb1<=x1 and x1<=x2 and x2<=xb2 and yb1<=y1 and y1<=y2 and y2<=yb2:\n",
    "                cutDefectBox = [x1,y1,x2,y2]   \n",
    "            else:\n",
    "                print('Error: Bonbox out of range: CutXY:%s; bndbox:%s'%(cutXY,bndbox))\n",
    "                cutDefectBox = [xb1,yb1,xb2,yb2] \n",
    "       \n",
    "        cutDefectArea = cal_area(cutDefectBox)\n",
    "        cutDefectRatio = round(cutDefectArea/defectArea,4)\n",
    "        if cutDefectBox!=[]:\n",
    "            cutDefectBox_ab = [cutDefectBox[0]-x1, cutDefectBox[1]-y1,cutDefectBox[2]-x1,cutDefectBox[3]-y1]#转换成绝对坐标\n",
    "            cutDefect = [defectType, cutDefectBox_ab, cutDefectRatio*defectRatio]\n",
    "            cutDefectList.append(cutDefect)\n",
    "     \n",
    "    if cutDefectList==[]:\n",
    "        cutDefectList.append(['正常',[0,0,x2-x1,y2-y1],1])\n",
    "    cutDict['defectList']= cutDefectList\n",
    "    cutDict['isNormalImg'] = True if cutDict['defectList'][0][0]=='正常' else False\n",
    "\n",
    "    return cutDict\n",
    "\n",
    "def sav_to_csv(xmlDict,csvSavPath):\n",
    "    head = ['filename','imgPath','isNormalImg','defectList','bndboxRatio','inBndboxArea']\n",
    "    #head = ['filename','imgPath','isNormalImg']\n",
    "    csvlist = []\n",
    "    for key in xmlDict: \n",
    "        csvlist.append(xmlDict[key])\n",
    "    \n",
    "    df = pd.DataFrame(columns=head, data=csvlist)\n",
    "    df.to_csv(csvSavPath,index=False,encoding=\"gbk\",)\n",
    "    return\n",
    "\n",
    "def draw_one_bndbox(img, bndbox, bndNum):#由于不能输入中文，所以框的text为其在xml中的序号\n",
    "    #用于将图片中的瑕疵框出，方便可视化\n",
    "    min_x,min_y,max_x,max_y = bndbox\n",
    "    cv2.rectangle(img,(min_x,min_y),(max_x,max_y),(255,0,0),3)\n",
    "    font=cv2.FONT_HERSHEY_SIMPLEX\n",
    "    cv2.putText(img, str(bndNum), (int((min_x+max_x)*0.5),int(min_y+(max_y-min_y)/4)), font, 1,(255,255,0),2)\n",
    "    return img\n",
    "\n",
    "def plt_img(img):\n",
    "    img_rgb = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n",
    "    #plt.figure(figsize=(20,20))\n",
    "    plt.figure()\n",
    "    plt.imshow(img_rgb)  \n",
    "    plt.show() \n",
    "    \n",
    "def sav_img(imgSavPath, img):\n",
    "    savDir = os.path.split(imgSavPath)[0]\n",
    "    if not os.path.exists(savDir):\n",
    "        os.makedirs(savDir)\n",
    "    cv2.imencode('.jpg', img)[1].tofile(imgSavPath)\n",
    "    return\n",
    "\n",
    "def cv_imread(filePath):\n",
    "    #由于路径有中文，所以用该函数读取\n",
    "    cv_img=cv2.imdecode(np.fromfile(filePath,dtype=np.uint8),-1)\n",
    "    ## imdecode读取的是rgb，如果后续需要opencv处理的话，需要转换成bgr，转换后图片颜色会变化\n",
    "    #cv_img=cv2.cvtColor(cv_img,cv2.COLOR_RGB2BGR)\n",
    "    return cv_img\n",
    "\n",
    "\n",
    "def copy_file(srcfile, savDir):\n",
    "    if not os.path.isfile(srcfile):#检测要复制的文件是否存在\n",
    "        pass\n",
    "    else:\n",
    "        if not os.path.exists(savDir):\n",
    "            os.makedirs(savDir)\n",
    "        filename = os.path.split(srcfile)[1]\n",
    "        shutil.copyfile(srcfile,savDir+'\\\\'+filename)\n",
    "    return True\n",
    "\n",
    "def random_split(splitDir, trainPerc):\n",
    "    #trainPerc：训练集所占总数的比例\n",
    "    imgList = glob.glob(splitDir+'\\\\*.jpg')\n",
    "    imgNum = len(imgList)\n",
    "    random.shuffle(imgList)\n",
    "    if imgNum ==1:\n",
    "        trainList = imgList\n",
    "        valicList = []\n",
    "    else:\n",
    "        i = max(round((1-trainPerc)*imgNum),1)\n",
    "        valicList = imgList[:i]\n",
    "        trainList = imgList[i:]\n",
    "        \n",
    "    #print('type:%5s, trainNum:%5d, valicNum:%5d'%(os.path.split(splitDir)[1],len(trainList),len(valicList)))\n",
    "    return trainList, valicList\n",
    "\n",
    "def gen_data_group(srcDir, savDir, typeNum=0, dirClear=True):\n",
    "    #typeNum: 选择输出多少类别的瑕疵，默认为0输出全部，比如为5，则输出数量最多的5个类别\n",
    "    #dirClear：是否先清空savDir中的所有文件\n",
    "    subDirList = []\n",
    "    for root,dirs,files in os.walk(srcDir):\n",
    "        subDirList.append(root)\n",
    "    del subDirList[0]#第一个是原目录，删除\n",
    "    imgNumList =[len(glob.glob(subDir+'\\\\*.jpg')) for subDir in subDirList]\n",
    "    idxSortedList = np.argsort(imgNumList)[::-1]#将子文件夹按文件多少从大到小排序\n",
    "    if typeNum!= 0:\n",
    "        typeNum = min(typeNum, len(subDirList))\n",
    "        idxSortedList = idxSortedList[:typeNum]\n",
    "    trainList = []\n",
    "    valicList = []\n",
    "    for i in idxSortedList:\n",
    "        subDir = subDirList[i]\n",
    "        if len(glob.glob(subDir+'\\\\*jpg'))>0:\n",
    "            subTrainList, subValicList = random_split(subDir, 0.9)\n",
    "            trainList +=subTrainList\n",
    "            valicList +=subValicList\n",
    "    #清空保存的文件夹\n",
    "    if dirClear ==True and os.path.exists(savDir):\n",
    "        shutil.rmtree(savDir)\n",
    "    #复制img同时生成json文件\n",
    "    trainSavDir = savDir +'\\\\train'\n",
    "    valicSavDir = savDir +'\\\\valic'\n",
    "    for i, imgPath in enumerate(trainList):\n",
    "        if copy_file(imgPath, trainSavDir)==0:\n",
    "            print('%s coppy failed.'%(imgPath))\n",
    "        xmlPath = imgPath.replace('jpg','xml')\n",
    "        jsonname = os.path.split(imgPath)[1].replace('.jpg','.json')\n",
    "        imgDict = gen_xmlDict(xmlPath)\n",
    "        with open(trainSavDir+'\\\\'+jsonname,'w',encoding='utf-8') as jsonfile:\n",
    "            json.dump(imgDict, jsonfile, ensure_ascii=False)\n",
    "        if i+1<len(trainList):\n",
    "            print('Splitting train set %d/%d'%(i+1,len(trainList)),end = '\\r')\n",
    "        else:\n",
    "            print('Splitting train set %d/%d'%(i+1,len(trainList)))\n",
    "        \n",
    "    for i, imgPath in enumerate(valicList):\n",
    "        if copy_file(imgPath, valicSavDir)==0:\n",
    "            print('%s coppy failed.'%(imgPath))\n",
    "        xmlPath = imgPath.replace('jpg','xml')\n",
    "        jsonname = os.path.split(imgPath)[1].replace('.jpg','.json')\n",
    "        imgDict = gen_xmlDict(xmlPath)\n",
    "        with open(valicSavDir+'\\\\'+jsonname,'w',encoding='utf-8') as jsonfile:\n",
    "            json.dump(imgDict, jsonfile, ensure_ascii=False)  \n",
    "        if i+1<len(valicList):\n",
    "            print('Splitting valic set %d/%d'%(i+1,len(valicList)),end = '\\r')\n",
    "        else:\n",
    "            print('Splitting valic set %d/%d'%(i+1,len(valicList)))\n",
    "        \n",
    "def gen_cutXY_list(w,cutw,step):\n",
    "    #根据切割大小，和步长，生成x或y坐标的list\n",
    "    a = list(range(0,w,step))\n",
    "    a.append(w-cutw)\n",
    "    a = list(set(a))#去重\n",
    "    a.sort() \n",
    "    xList = a[:(a.index(w-cutw)+1)]\n",
    "    return xList\n",
    "    \n",
    "def cut_step(imgPath, cuth, cutw, cutStep, defectAreaP, normalNumP,cutRamdomList, savDir, drawBox=False):\n",
    "    filename = os.path.split(imgPath)[1]\n",
    "    img = cv_imread(imgPath)\n",
    "    h,w,c = img.shape\n",
    "    xList = gen_cutXY_list(w,cutw,cutStep)\n",
    "    yList = gen_cutXY_list(h,cuth,cutStep)\n",
    "    cutId = 0\n",
    "    jsonPath = imgPath.replace('jpg','json')\n",
    "    with open(jsonPath,'r',encoding='utf-8') as jsonfile:\n",
    "        imgDict=json.load(jsonfile)\n",
    "        defectList = imgDict['defectList']\n",
    "    for y1 in yList:\n",
    "        for x1 in xList:\n",
    "            cutFilename = filename[:-4]+'_'+str(cutId)+'.jpg'\n",
    "            cutJsonname = cutFilename.replace('jpg','json')\n",
    "            x2,y2 = x1+cutw,y1+cuth\n",
    "            cutXY = [x1,y1,x2,y2]\n",
    "            cutImg = img[y1:y2,x1:x2]\n",
    "            cutDict = gen_cutDict(cutXY, defectList)\n",
    "            cutDefectList = cutDict['defectList']\n",
    "            if drawBox==True and cutDefectList[0][0]!='正常':\n",
    "                for i,cutDefect in enumerate(cutDefectList):\n",
    "                    draw_one_bndbox(cutImg, cutDefect[1], i)\n",
    "            if cutDefectList[0][0]!='正常':\n",
    "                for cutDefect in cutDefectList:\n",
    "                    if cutDefect[2]>=defectAreaP:\n",
    "                        cutImgPath = savDir +'\\\\defect\\\\'+cutFilename\n",
    "                        sav_img(cutImgPath, cutImg)\n",
    "                        break\n",
    "            else:\n",
    "                if cutRamdomList[cutId]>normalNumP:\n",
    "                    cutImgPath = savDir +'\\\\normal\\\\'+cutFilename\n",
    "                    sav_img(cutImgPath, cutImg)\n",
    "            #with open(savDir+'\\\\'+cutJsonname,'w',encoding='utf-8') as jsonfile:\n",
    "                #json.dump(cutDict, jsonfile, ensure_ascii=False)\n",
    "            cutId+=1   \n",
    "    return\n",
    "\n",
    "def gen_cut_step(imgDir, cuth, cutw, cutStep, defectAreaP, normalNumP, savDir, drawBox=False):\n",
    "#defectAreaP：如：0.09的含义是，若切割后的图片中的瑕疵面积占原瑕疵面积的9%以上，则认为该瑕疵足够大，保存在defect文件中，否则舍弃\n",
    "#normalNumP：舍弃掉的正常图片的比例\n",
    "#drawBox：是否在生产的切割图片中将瑕疵框出来\n",
    "    \n",
    "    imgPathList = glob.glob(imgDir+'\\\\*.jpg')\n",
    "    h,w,c = cv2.imread(imgPathList[0]).shape\n",
    "    xList = gen_cutXY_list(w,cutw,cutStep)\n",
    "    yList = gen_cutXY_list(h,cuth,cutStep)\n",
    "    cutNum = len(xList)*len(yList)*len(imgPathList)\n",
    "    randomList = [random.random() for i in range(cutNum)]\n",
    "    for i,imgPath in enumerate(imgPathList):\n",
    "        cutRandomList = randomList[i*len(xList)*len(yList):(i+1)*len(xList)*len(yList)]\n",
    "        cut_step(imgPath, cuth,cutw,cutStep, defectAreaP, normalNumP, cutRandomList, savDir, drawBox=drawBox)\n",
    "        if i+1<len(imgPathList):\n",
    "            print('Cutting img %d/%d'%(i+1,len(imgPathList)),end = '\\r')\n",
    "        else:\n",
    "            print('Cutting img %d/%d'%(i+1,len(imgPathList)))\n",
    "\n",
    "def gen_resize(imgDir, resize, savDir):\n",
    "    imgPathList = glob.glob(imgDir+'\\\\*.jpg')\n",
    "    h,w = resize\n",
    "    for i,imgPath in enumerate(imgPathList):\n",
    "        filename = os.path.split(imgPath)[1]\n",
    "        img = cv2.imread(imgPath)\n",
    "        img = cv2.resize(img, (w,h), interpolation=cv2.INTER_AREA)\n",
    "        jsonPath = imgPath.replace('jpg','json')\n",
    "        with open(jsonPath,'r',encoding='utf-8') as jsonfile:\n",
    "            imgDict=json.load(jsonfile)\n",
    "            if imgDict['defectList'][0][0]=='正常':\n",
    "                imgPath = savDir+'\\\\normal\\\\'+filename\n",
    "            else:\n",
    "                imgPath = savDir+'\\\\defect\\\\'+filename\n",
    "        sav_img(imgPath, img)\n",
    "        if i+1<len(imgPathList):\n",
    "            print('Resizing img %d/%d'%(i+1,len(imgPathList)),end = '\\r')\n",
    "        else:\n",
    "            print('Resizing img %d/%d'%(i+1,len(imgPathList)))\n",
    "    \n",
    "def predictCutPic(picPath, cutH, cutW, cutStep, model, padding=False, paddingSize=160):\n",
    "    #用于分割图片的预测\n",
    "    img = cv2.imread(picPath)\n",
    "    if padding == True:\n",
    "        img= cv2.copyMakeBorder(img,paddingSize,paddingSize,paddingSize,paddingSize,cv2.BORDER_CONSTANT,value=0)\n",
    "    h,w,c= img.shape\n",
    "    cutNum = int((h-cutH+cutStep)/cutStep)*int((w-cutW+cutStep)/cutStep)\n",
    "    cutImgBatch = np.zeros((cutNum,cutH,cutW,3))\n",
    "    i=0\n",
    "    for y1 in range(0,h-cutH+cutStep, cutStep):\n",
    "        for x1 in range(0,w-cutW+cutStep, cutStep):\n",
    "            x2,y2 = x1+cutW, y1+cutH\n",
    "            cutImg = img[y1:y2, x1:x2]\n",
    "            cutImgBatch[i] = cutImg/255.\n",
    "            i+=1\n",
    "    pArray = model.predict(cutImgBatch)\n",
    "    return pArray[:,0]\n",
    "\n",
    "def predictFullPic(picPath,model):\n",
    "    #用于整张图片进行resize的预测\n",
    "    img = cv2.imread(picPath)\n",
    "    img = cv2.resize(img, (800,600) ,interpolation=cv2.INTER_AREA)\n",
    "    h,w,c = img.shape\n",
    "    p = model.predict((img/255.).reshape(1,h,w,c))[0][0]\n",
    "    return p\n",
    "\n",
    "def deal_pList(pList):#处理p，将大于等于1的和小于等于0的变成0到1之间，并保存成6位小数，防止提交结果报错\n",
    "    pListNew = []\n",
    "    for p in pList:\n",
    "        if p<=0:\n",
    "            p = 0.000001\n",
    "        elif p>=0.999998:\n",
    "            p = 0.999999 \n",
    "        else:\n",
    "            p = math.ceil(p*1e6)/1e6\n",
    "        pListNew.append(p)\n",
    "    return pListNew     \n",
    "\n",
    "def search_dir(dirPath, suffix):\n",
    "    suffixList = []\n",
    "    for (root, dirs, files)in os.walk(dirPath):\n",
    "            findList = glob.glob(root+'\\\\*.'+suffix)\n",
    "            for f in findList:\n",
    "                suffixList.append(f)\n",
    "    return suffixList    \n",
    "\n",
    "def plt_auc(pList, yList):\n",
    "    auc = metrics.roc_auc_score(yList, pList)\n",
    "    print('auc: %f'%auc)\n",
    "    mean_tpr = 0.0\n",
    "    mean_fpr = np.linspace(0,1,100)\n",
    "    all_tpr = []\n",
    "    fpr,tpr,thresholds = metrics.roc_curve(yList, pList)\n",
    "    mean_tpr +=interp(mean_fpr, fpr, tpr)\n",
    "    mean_tpr[0]=0.0\n",
    "    roc_auc=metrics.auc(fpr,tpr)\n",
    "    plt.plot(fpr,tpr,lw=1,label='ROC fold %d (area = %0.2f)' % (len(pList), roc_auc))\n",
    "    plt.show()\n",
    "    return auc"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Split train and valic set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Splitting train set 1804/1804\n",
      "Splitting valic set 218/218\n"
     ]
    }
   ],
   "source": [
    "rawDir=r'.\\data\\raw'\n",
    "gen_data_group(r'.\\data\\official', rawDir)#按9：1分配保存保存完整图片并提取对应xml参数保存成同名json文件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cut and Save img for Model A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cutting img 1804/1804\n",
      "Cutting img 218/218\n"
     ]
    }
   ],
   "source": [
    "cutDir = r'.\\data\\cut'\n",
    "#按320的步长将图片切割成320*320大小，1张图片将被切割成6*8=48张\n",
    "#并将包含瑕疵的图片分到defect文件夹中，并随机选择5%的正常图片保到normal文件夹中，使正常图片约为瑕疵图片的2倍\n",
    "gen_cut_step(rawDir+'\\\\train',320, 320, 320,0.09,0.95, cutDir+'\\\\train')\n",
    "gen_cut_step(rawDir+'\\\\valic',320, 320, 320,0.09,0.94, cutDir+'\\\\valic')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Resize and Save img for Model B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Resizing img 1804/1804\n",
      "Resizing img 218/218\n"
     ]
    }
   ],
   "source": [
    "resizeDir = r'.\\data\\resize'\n",
    "gen_resize(rawDir+'\\\\train', (600,800), resizeDir+'\\\\train')\n",
    "gen_resize(rawDir+'\\\\valic', (600,800), resizeDir+'\\\\valic')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Train Model A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\h5py\\__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n",
      "  from ._conv import register_converters as _register_converters\n",
      "Using TensorFlow backend.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 6515 images belonging to 2 classes.\n",
      "Found 949 images belonging to 2 classes.\n"
     ]
    }
   ],
   "source": [
    "#img gen\n",
    "from keras.preprocessing.image import ImageDataGenerator\n",
    "from keras.preprocessing import image\n",
    "train_datagen = ImageDataGenerator(rescale=1./255,\n",
    "                                   shear_range=0.2,\n",
    "                                   zoom_range=0.1,\n",
    "                                   horizontal_flip=True,\n",
    "                                   vertical_flip=True,\n",
    "                                   rotation_range = 90,\n",
    "                                   fill_mode = 'constant',\n",
    "                                   width_shift_range=0.1,\n",
    "                                   height_shift_range=0.1,\n",
    "                                   channel_shift_range=10,\n",
    "                                   cval = 0)\n",
    "\n",
    "valic_datagen = ImageDataGenerator(rescale=1./255)\n",
    "\n",
    "trainGen320 = train_datagen.flow_from_directory(\n",
    "        r'.\\data\\cut\\train',\n",
    "        target_size=(320,320),\n",
    "        batch_size=24,\n",
    "        seed = seed,\n",
    "        class_mode='categorical')\n",
    "\n",
    "valicGen320 = valic_datagen.flow_from_directory(\n",
    "        r'.\\data\\cut\\valic',\n",
    "        target_size=(320,320),\n",
    "        batch_size=24,\n",
    "        seed = seed,\n",
    "        class_mode='categorical')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "#set model layers\n",
    "import keras\n",
    "from keras.models import Model, Sequential \n",
    "from keras.layers import Dense, Activation, Conv2D, MaxPooling2D, Flatten, Dropout, GlobalAveragePooling2D \n", 
    "from keras.applications.inception_resnet_v2 import InceptionResNetV2\n",
    "from keras.applications.inception_resnet_v2 import preprocess_input, decode_predictions\n",
    "\n",
    "modelA = InceptionResNetV2(include_top=False,weights='imagenet',input_shape=(None, None, 3))\n",
    "x = modelA.output\n",
    "x = GlobalAveragePooling2D()(x)\n",
    "x = Dense(1024, activation='relu')(x)\n",
    "x = Dropout(0.5, seed=seed)(x)\n",
    "predictions = Dense(2, activation='softmax')(x)\n",
    "modelA = Model(inputs=modelA.input, outputs=predictions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:Variable *= will be deprecated. Use variable.assign_mul if you want assignment to the variable value or 'x = x * y' if you want a new python Tensor object.\n",
      "Epoch 1/20\n",
      "272/272 [==============================] - 207s 760ms/step - loss: 0.6390 - acc: 0.6450 - val_loss: 0.6517 - val_acc: 0.6038\n",
      "\n",
      "Epoch 00001: val_acc improved from -inf to 0.60379, saving model to ./h5/cut320-01-0.6038.h5\n",
      "Epoch 2/20\n",
      "272/272 [==============================] - 167s 615ms/step - loss: 0.5711 - acc: 0.7126 - val_loss: 0.5328 - val_acc: 0.7482\n",
      "\n",
      "Epoch 00002: val_acc improved from 0.60379 to 0.74816, saving model to ./h5/cut320-02-0.7482.h5\n",
      "Epoch 3/20\n",
      "272/272 [==============================] - 167s 615ms/step - loss: 0.5107 - acc: 0.7582 - val_loss: 0.4717 - val_acc: 0.8019\n",
      "\n",
      "Epoch 00003: val_acc improved from 0.74816 to 0.80190, saving model to ./h5/cut320-03-0.8019.h5\n",
      "Epoch 4/20\n",
      "272/272 [==============================] - 167s 616ms/step - loss: 0.4751 - acc: 0.7873 - val_loss: 0.4329 - val_acc: 0.8261\n",
      "\n",
      "Epoch 00004: val_acc improved from 0.80190 to 0.82613, saving model to ./h5/cut320-04-0.8261.h5\n",
      "Epoch 5/20\n",
      "272/272 [==============================] - 167s 615ms/step - loss: 0.4480 - acc: 0.8013 - val_loss: 0.4039 - val_acc: 0.8303\n",
      "\n",
      "Epoch 00005: val_acc improved from 0.82613 to 0.83035, saving model to ./h5/cut320-05-0.8303.h5\n",
      "Epoch 6/20\n",
      "272/272 [==============================] - 167s 616ms/step - loss: 0.4321 - acc: 0.8106 - val_loss: 0.3850 - val_acc: 0.8462\n",
      "\n",
      "Epoch 00006: val_acc improved from 0.83035 to 0.84615, saving model to ./h5/cut320-06-0.8462.h5\n",
      "Epoch 7/20\n",
      "272/272 [==============================] - 167s 616ms/step - loss: 0.4147 - acc: 0.8200 - val_loss: 0.4008 - val_acc: 0.8282\n",
      "\n",
      "Epoch 00007: val_acc did not improve\n",
      "Epoch 8/20\n",
      "272/272 [==============================] - 168s 616ms/step - loss: 0.4094 - acc: 0.8238 - val_loss: 0.3837 - val_acc: 0.8451\n",
      "\n",
      "Epoch 00008: val_acc did not improve\n",
      "Epoch 9/20\n",
      "272/272 [==============================] - 167s 616ms/step - loss: 0.3955 - acc: 0.8355 - val_loss: 0.3978 - val_acc: 0.8303\n",
      "\n",
      "Epoch 00009: val_acc did not improve\n",
      "Epoch 10/20\n",
      "272/272 [==============================] - 167s 616ms/step - loss: 0.3870 - acc: 0.8298 - val_loss: 0.3726 - val_acc: 0.8419\n",
      "\n",
      "Epoch 00010: val_acc did not improve\n",
      "Epoch 11/20\n",
      "272/272 [==============================] - 167s 615ms/step - loss: 0.3731 - acc: 0.8408 - val_loss: 0.3929 - val_acc: 0.8440\n",
      "\n",
      "Epoch 00011: val_acc did not improve\n",
      "Epoch 12/20\n",
      "272/272 [==============================] - 168s 616ms/step - loss: 0.3692 - acc: 0.8494 - val_loss: 0.3705 - val_acc: 0.8462\n",
      "\n",
      "Epoch 00012: val_acc did not improve\n",
      "Epoch 13/20\n",
      "272/272 [==============================] - 167s 616ms/step - loss: 0.3611 - acc: 0.8516 - val_loss: 0.3833 - val_acc: 0.8303\n",
      "\n",
      "Epoch 00013: val_acc did not improve\n",
      "Epoch 14/20\n",
      "272/272 [==============================] - 167s 616ms/step - loss: 0.3560 - acc: 0.8529 - val_loss: 0.3568 - val_acc: 0.8546\n",
      "\n",
      "Epoch 00014: val_acc improved from 0.84615 to 0.85458, saving model to ./h5/cut320-14-0.8546.h5\n",
      "Epoch 15/20\n",
      "272/272 [==============================] - 167s 615ms/step - loss: 0.3488 - acc: 0.8552 - val_loss: 0.3717 - val_acc: 0.8535\n",
      "\n",
      "Epoch 00015: val_acc did not improve\n",
      "Epoch 16/20\n",
      "272/272 [==============================] - 167s 615ms/step - loss: 0.3485 - acc: 0.8552 - val_loss: 0.3655 - val_acc: 0.8535\n",
      "\n",
      "Epoch 00016: val_acc did not improve\n",
      "Epoch 17/20\n",
      "272/272 [==============================] - 167s 615ms/step - loss: 0.3369 - acc: 0.8587 - val_loss: 0.3677 - val_acc: 0.8525\n",
      "\n",
      "Epoch 00017: val_acc did not improve\n",
      "Epoch 18/20\n",
      "272/272 [==============================] - 167s 615ms/step - loss: 0.3350 - acc: 0.8614 - val_loss: 0.3537 - val_acc: 0.8577\n",
      "\n",
      "Epoch 00018: val_acc improved from 0.85458 to 0.85774, saving model to ./h5/cut320-18-0.8577.h5\n",
      "Epoch 19/20\n",
      "272/272 [==============================] - 167s 616ms/step - loss: 0.3235 - acc: 0.8637 - val_loss: 0.3686 - val_acc: 0.8577\n",
      "\n",
      "Epoch 00019: val_acc improved from 0.85774 to 0.85774, saving model to ./h5/cut320-19-0.8577.h5\n",
      "Epoch 20/20\n",
      "272/272 [==============================] - 167s 615ms/step - loss: 0.3202 - acc: 0.8679 - val_loss: 0.3773 - val_acc: 0.8493\n",
      "\n",
      "Epoch 00020: val_acc did not improve\n"
     ]
    }
   ],
   "source": [
    "#train model \n",
    "from keras.optimizers import Adam, SGD\n",
    "from keras.layers import *\n",
    "from keras.models import *\n",
    "from keras.optimizers import *\n",
    "from keras.callbacks import *\n",
    "from keras.callbacks import ModelCheckpoint\n",
    "resultsA=[]\n",
    "optimizer = SGD(lr=0.001, momentum=0.9,  decay=1e-6, nesterov=True)\n",
    "modelA.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy'])\n",
    "\n",
    "filepath=r\"./h5/cut320-{epoch:02d}-{val_acc:.4f}.h5\"\n",
    "checkpoint= ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_best_only=True, mode='max')\n",
    "callbacks_list= [checkpoint]\n",
    "\n",
    "resultA = modelA.fit_generator(\n",
    "        trainGen320,\n",
    "        epochs=20,verbose=1,\n",
    "        callbacks=callbacks_list,\n",
    "        validation_data=valicGen320,\n",
    "        class_weight='auto')\n",
    "resultsA.append(resultA)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "modelA.save_weights(r'./h5/cut320.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1ed27d1a0f0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(resultA.history['acc'],'b')\n",
    "plt.plot(resultA.history['val_acc'],'r')\n",
    "plt.plot(resultA.history['loss'],'b')\n",
    "plt.plot(resultA.history['val_loss'],'r')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Train Model B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 1804 images belonging to 2 classes.\n",
      "Found 218 images belonging to 2 classes.\n"
     ]
    }
   ],
   "source": [
    "#img gen\n",
    "trainGen600 = train_datagen.flow_from_directory(\n",
    "        r'.\\data\\resize\\train',\n",
    "        target_size=(600,800),\n",
    "        batch_size=4,\n",
    "        seed = seed,\n",
    "        class_mode='categorical')\n",
    "\n",
    "valicGen600 = valic_datagen.flow_from_directory(\n",
    "        r'.\\data\\resize\\valic',\n",
    "        target_size=(600,800),\n",
    "        batch_size=4,\n",
    "        seed = seed,\n",
    "        class_mode='categorical')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "#set model layers\n",
    "modelB = InceptionResNetV2(include_top=False,weights='imagenet',input_shape=(None,None,3))\n",
    "x = modelB.output\n",
    "x = GlobalAveragePooling2D()(x)\n",
    "x = Dense(1024, activation='relu')(x)\n",
    "x = Dropout(0.5,seed=seed)(x)\n",
    "predictions = Dense(2, activation='softmax')(x)\n",
    "modelB = Model(inputs=modelB.input, outputs=predictions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n",
      "451/451 [==============================] - 298s 660ms/step - loss: 0.6315 - acc: 0.6624 - val_loss: 0.6051 - val_acc: 0.6972\n",
      "\n",
      "Epoch 00001: val_acc improved from -inf to 0.69725, saving model to ./h5/resize600-01-0.6972.h5\n",
      "Epoch 2/10\n",
      "451/451 [==============================] - 255s 566ms/step - loss: 0.5522 - acc: 0.7445 - val_loss: 0.5575 - val_acc: 0.7569\n",
      "\n",
      "Epoch 00002: val_acc improved from 0.69725 to 0.75688, saving model to ./h5/resize600-02-0.7569.h5\n",
      "Epoch 3/10\n",
      "451/451 [==============================] - 256s 567ms/step - loss: 0.4509 - acc: 0.8099 - val_loss: 0.4725 - val_acc: 0.8073\n",
      "\n",
      "Epoch 00003: val_acc improved from 0.75688 to 0.80734, saving model to ./h5/resize600-03-0.8073.h5\n",
      "Epoch 4/10\n",
      "451/451 [==============================] - 256s 567ms/step - loss: 0.4213 - acc: 0.8282 - val_loss: 0.3943 - val_acc: 0.8211\n",
      "\n",
      "Epoch 00004: val_acc improved from 0.80734 to 0.82110, saving model to ./h5/resize600-04-0.8211.h5\n",
      "Epoch 5/10\n",
      "451/451 [==============================] - 256s 567ms/step - loss: 0.4110 - acc: 0.8293 - val_loss: 0.3749 - val_acc: 0.8165\n",
      "\n",
      "Epoch 00005: val_acc did not improve\n",
      "Epoch 6/10\n",
      "451/451 [==============================] - 256s 568ms/step - loss: 0.3766 - acc: 0.8453 - val_loss: 0.3874 - val_acc: 0.8394\n",
      "\n",
      "Epoch 00006: val_acc improved from 0.82110 to 0.83945, saving model to ./h5/resize600-06-0.8394.h5\n",
      "Epoch 7/10\n",
      "451/451 [==============================] - 256s 568ms/step - loss: 0.3626 - acc: 0.8631 - val_loss: 0.4209 - val_acc: 0.8486\n",
      "\n",
      "Epoch 00007: val_acc improved from 0.83945 to 0.84862, saving model to ./h5/resize600-07-0.8486.h5\n",
      "Epoch 8/10\n",
      "451/451 [==============================] - 256s 568ms/step - loss: 0.3518 - acc: 0.8586 - val_loss: 0.4899 - val_acc: 0.7844\n",
      "\n",
      "Epoch 00008: val_acc did not improve\n",
      "Epoch 9/10\n",
      "451/451 [==============================] - 256s 568ms/step - loss: 0.3379 - acc: 0.8664 - val_loss: 0.4304 - val_acc: 0.8028\n",
      "\n",
      "Epoch 00009: val_acc did not improve\n",
      "Epoch 10/10\n",
      "451/451 [==============================] - 256s 568ms/step - loss: 0.3271 - acc: 0.8786 - val_loss: 0.3795 - val_acc: 0.8440\n",
      "\n",
      "Epoch 00010: val_acc did not improve\n"
     ]
    }
   ],
   "source": [
    "#train model\n",
    "resultsB=[]\n",
    "optimizer = SGD(lr=0.001, momentum=0.9,  decay=1e-6, nesterov=True)\n",
    "modelB.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy'])\n",
    "\n",
    "filepath=r\"./h5/resize600-{epoch:02d}-{val_acc:.4f}.h5\"\n",
    "checkpoint= ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_best_only=True, mode='max')\n",
    "callbacks_list= [checkpoint]\n",
    "\n",
    "resultB = modelB.fit_generator(\n",
    "        trainGen600,\n",
    "        epochs=10,verbose=1,\n",
    "        callbacks=callbacks_list,\n",
    "        validation_data=valicGen600,\n",
    "        class_weight='auto')\n",
    "resultsB.append(resultB)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "modelB.save_weights(r'./h5/resize600.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1ed27d1deb8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(resultB.history['acc'],'b')\n",
    "plt.plot(resultB.history['val_acc'],'r')\n",
    "plt.plot(resultB.history['loss'],'b')\n",
    "plt.plot(resultB.history['val_loss'],'r')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Test A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|████████████████████████████████████████████████████████████████████████████████| 662/662 [27:19<00:00,  2.48s/it]\n"
     ]
    }
   ],
   "source": [
    "from tqdm import tqdm\n",
    "\n",
    "testDir = r'.\\test\\testa'\n",
    "imgPathList = search_dir(testDir, 'jpg')\n",
    "xmlPathList = search_dir(testDir, 'xml')\n",
    "xmlNameList = [os.path.split(xmlPath)[1][:-4] for xmlPath in xmlPathList]\n",
    "yList = []\n",
    "for imgPath in imgPathList:\n",
    "    imgname = os.path.split(imgPath)[1]\n",
    "    if imgname[:-4] in xmlNameList:\n",
    "        yList.append(1)\n",
    "    else:\n",
    "        yList.append(0)\n",
    "\n",
    "pArrayListA = []\n",
    "pListB = []\n",
    "for imgPath in tqdm(imgPathList):\n",
    "    pArrayA = predictCutPic(imgPath,320,320,160,modelA,padding=False,paddingSize=160)\n",
    "    pB = predictFullPic(imgPath,modelB)\n",
    "    pArrayListA.append(pArrayA)\n",
    "    pListB.append(pB)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "auc: 0.951915\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1eab88ab208>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "pListA = [np.mean(np.sort(pArray)[-3:]) for pArray in pArrayListA]\n",
    "pList = [0.5*pA+0.5*pB for pA,pB in zip(pListA,pListB)]\n",
    "\n",
    "auc = plt_auc(pList, yList)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "done\n"
     ]
    }
   ],
   "source": [
    "import datetime\n",
    "#save test data to csv\n",
    "pList = deal_pList(pList)\n",
    "predictDf = pd.DataFrame()\n",
    "predictDf['filename'] = [os.path.split(imgPath)[1] for imgPath in imgPathList]\n",
    "predictDf['probability'] = pList\n",
    "predictDf.to_csv(r'.\\submit\\submit_'+datetime.datetime.now().strftime('%Y%m%d_%H%M%S') + '.csv', header=None, index=False)\n",
    "print('done')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Test B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|████████████████████████████████████████████████████████████████████████████████| 647/647 [26:37<00:00,  2.47s/it]\n"
     ]
    }
   ],
   "source": [
    "from tqdm import tqdm\n",
    "\n",
    "testDir = r'.\\test\\testb'\n",
    "imgPathList = search_dir(testDir, 'jpg')\n",
    "xmlPathList = search_dir(testDir, 'xml')\n",
    "xmlNameList = [os.path.split(xmlPath)[1][:-4] for xmlPath in xmlPathList]\n",
    "yList = []\n",
    "for imgPath in imgPathList:\n",
    "    imgname = os.path.split(imgPath)[1]\n",
    "    if imgname[:-4] in xmlNameList:\n",
    "        yList.append(1)\n",
    "    else:\n",
    "        yList.append(0)\n",
    "\n",
    "pArrayListA = []\n",
    "pListB = []\n",
    "for imgPath in tqdm(imgPathList):\n",
    "    pArrayA = predictCutPic(imgPath,320,320,160,modelA,padding=False,paddingSize=160)\n",
    "    pB = predictFullPic(imgPath,modelB)\n",
    "    pArrayListA.append(pArrayA)\n",
    "    pListB.append(pB)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "auc: 0.953338\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1ed9cc5f5f8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "pListA = [np.mean(np.sort(pArray)[-3:]) for pArray in pArrayListA]\n",
    "pList = [0.5*pA+0.5*pB for pA,pB in zip(pListA,pListB)]\n",
    "\n",
    "auc = plt_auc(pList, yList)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "done\n"
     ]
    }
   ],
   "source": [
    "import datetime\n",
    "#save test data to csv\n",
    "pList = deal_pList(pList)\n",
    "predictDf = pd.DataFrame()\n",
    "predictDf['filename'] = [os.path.split(imgPath)[1] for imgPath in imgPathList]\n",
    "predictDf['probability'] = pList\n",
    "predictDf.to_csv(r'.\\submit\\submit_'+datetime.datetime.now().strftime('%Y%m%d_%H%M%S') + '.csv', header=None, index=False)\n",
    "print('done')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
